import pandas as pd
import streamlit as st
from code import code_two
import pandas as pd

# 设置页面布局为宽屏模式
st.set_page_config(layout="wide")

choice = st.sidebar.selectbox("请选择",
                              ["介绍", "Sigmoid函数", "tanh函数", "ReLU函数", "Leaky ReLU函数", "PReLU函数", "ELU函数",
                               "Softmax函数", "总结"])
if choice == "介绍":
    st.title("激活函数")
    with st.form("form1"):
        st.markdown("""
        ## 一、激活函数介绍 ##
        激活函数是深度学习模型（尤其是神经网络）中的核心组件，其核心作用是为网络引入非线性变换能力—— 若没有激活函数，无论神经网络有多少层，最终都等价于单层线性模型，无法拟合复杂的数据模式（如图像、自然语言中的非线性关系）。
        """)
        st.markdown("""
        ## 二、激活函数的核心作用 ##
        - 引入非线性：突破线性模型的局限性，让网络能学习复杂函数（如分类边界、时序依赖等）。
        - 控制梯度流动：合理的激活函数可避免梯度消失 / 爆炸，保证深层网络的训练稳定性。
        - 输出映射：将神经元的线性计算结果（如 wx + b）映射到特定范围（如 [0,1] 用于概率输出，[-1,1] 用于特征归一化）。
        """)
        st.markdown("""
        ## 三、常见激活函数及特性对比 ##
        """)
        activation_info = [
            {
                "名称": "Sigmoid",
                "数学表达式": r"f(x) = \frac{1}{1 + e^{-x}}",
                "函数图像特点": "S形曲线，输出范围∈(0,1)，在x=0处导数最大（0.25）",
                "适用场景": "二分类任务输出层（输出可直接解释为概率）",
                "优点": "连续可导；输出值有概率意义，易理解",
                "缺点": "x→±∞时梯度趋近于0（梯度消失）；输出非零均值，影响梯度更新稳定性"
            },
            {
                "名称": "Tanh",
                "数学表达式": r"f(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} \quad (\text{等价于 } 2\text{Sigmoid}(2x)-1)",
                "函数图像特点": "S形曲线，输出范围∈(-1,1)，x=0处过原点，导数最大为1",
                "适用场景": "替代Sigmoid用于隐藏层（尤其小规模网络）",
                "优点": "输出零均值，梯度比Sigmoid更陡，缓解均值偏移问题",
                "缺点": "x→±∞时仍存在梯度消失问题；计算复杂度略高于Sigmoid"
            },
            {
                "名称": "ReLU",
                "数学表达式": r"f(x) = \max(0, x)",
                "函数图像特点": "左半段（x<0）输出为0，右半段（x>0）线性增长，x=0处不可导但不影响训练",
                "适用场景": "卷积神经网络（CNN）隐藏层、大规模深层网络",
                "优点": "计算极快（仅比较操作）；x>0时梯度恒为1，缓解梯度消失；稀疏激活（减少冗余计算）",
                "缺点": "死亡ReLU问题（x<0时神经元永久失活，无法更新）"
            },
            {
                "名称": "Leaky ReLU",
                "数学表达式": r"f(x) = \max(\alpha x, x) \quad (\alpha通常取0.01)",
                "函数图像特点": "左半段（x<0）微弱线性（αx），右半段（x>0）线性增长，全区间可导",
                "适用场景": "解决死亡ReLU问题的深层网络（如CNN、全连接网络）",
                "优点": "保留负区间梯度，避免神经元永久失活；计算速度接近ReLU",
                "缺点": "α需手动调参（无自适应能力）；负区间梯度微弱，仍可能更新缓慢"
            },
            {
                "名称": "ELU",
                "数学表达式": r"f(x) = \begin{cases} x & x>0 \\ \alpha(e^x - 1) & x \leq 0 \end{cases} \quad (\alpha通常取1.0)",
                "函数图像特点": "左半段（x≤0）指数趋近-α，右半段（x>0）线性增长，平滑过渡",
                "适用场景": "对噪声敏感的深层网络（如ResNet、对抗生成网络）",
                "优点": "抗噪声能力强；输出更接近零均值；x→-∞时梯度趋近于0，避免极端值干扰",
                "缺点": "计算复杂度高于ReLU（需指数运算）；α需手动调参"
            },
            {
                "名称": "Softmax",
                "数学表达式": r"f(x_i) = \frac{e^{x_i}}{\sum_{j=1}^K e^{x_j}} \quad (K为类别数)",
                "函数图像特点": "多输入单输出（K个输入对应K个输出），所有输出和为1，输出∈(0,1)",
                "适用场景": "多分类任务输出层（如图像分类、文本分类）",
                "优点": "直接输出类别概率分布，易理解；与交叉熵损失搭配时梯度计算稳定",
                "缺点": "易受异常值影响（e^x放大极端值）；不适合作为隐藏层激活函数"
            },
            {
                "名称": "GELU",
                "数学表达式": r"f(x) = x \cdot \Phi(x) \quad (\Phi(x)是标准正态分布的CDF)",
                "函数图像特点": "平滑的非线性曲线，x<0时接近0，x>0时接近x，梯度自适应变化",
                "适用场景": "Transformer架构（如BERT、GPT）、自然语言处理（NLP）模型",
                "优点": "自适应梯度（输入越大梯度越接近1，输入越小梯度越接近0）；训练稳定性高",
                "缺点": "计算稍复杂（需正态分布累积概率）；x→-∞时梯度趋近于0"
            }
        ]
        # 表头
        cols = st.columns([7, 8, 8, 8, 8, 8, 8])
        headers = ["名称", "数学表达式", "函数图像特点", "适用场景", "优点", "缺点"]
        for col, header in zip(cols, headers):
            col.markdown(f"**{header}**")

        st.markdown("---")

        # 循环输出表格内容
        for item in activation_info:
            cols = st.columns([2, 3, 3, 3, 3, 3, 3])
            cols[0].write(item["名称"])
            cols[1].latex(item["数学表达式"])
            cols[2].write(item["函数图像特点"])
            cols[3].write(item["适用场景"])
            cols[4].write(item["优点"])
            cols[5].write(item["缺点"])
            st.markdown("---")
        col1, col2 = st.columns([10, 1])
        with col2:
            submit = st.form_submit_button("我已学习")
        if submit:
            st.success("恭喜你，你已看完本小节内容")
            st.balloons()
elif choice == "Sigmoid函数":
    st.title("Sigmoid函数")
    with st.form("Sigmoid"):
        st.markdown("## 一、Sigmoid函数介绍 ##")
        st.markdown('#### Sigmoid函数是常用的激活函数之一，它的数学表达式为： ####')
        st.latex(r"f(x) = \frac{1}{1 + e^{-x}}")
        st.markdown("""
        ### 1. Sigmoid：最早的激活函数之一 ##
        - 核心用途：二分类任务的输出层（如 “是否为猫”，输出 0→非猫，1→猫）。
        #### 问题： ####
        - 梯度消失：当输入 x>5 或 x<-5 时，导数趋近于 0，深层网络的梯度无法传递到浅层。
        - 输出偏移：所有输出均为正（∈(0,1)），导致下一层输入的均值非零，梯度更新方向不稳定。
        #### 现状： ####
        - 仅在简单二分类输出层使用，已基本退出隐藏层。
        """)
        col1, col2 = st.columns([10, 1])
        with col2:
            submit = st.form_submit_button("我已了解")
        if submit:
            st.success("恭喜你，请往下查看代码示例")
            st.balloons()
    with st.form("Sigmoid_2"):
        st.markdown("## 二、Sigmoid函数图像 ##")
        sample_code = code_two.sigmoid_code()
        with st.expander("点击展开查看代码"):
            st.code(sample_code, language="python")
        col1, col2 = st.columns([6, 1])
        with col2:
            submit = st.form_submit_button("我已知晓，运行代码")
        if submit:
            st.success("已运行该代码，结果显示如下：")
            code_two.sigmoid_run()
elif choice == "tanh函数":
    st.title("tanh函数")
    with st.form("tanh"):
        st.markdown("""
        ## 一、tanh函数介绍 ##
        tanh 函数（Hyperbolic Tangent Function，双曲正切函数）是数学中双曲函数的核心成员，因非线性、有限值域及零中心化输出等特性，
        在机器学习、信号处理、物理学等领域被广泛应用，尤其成为神经网络激活函数的经典选择之一。
        tanh 函数的本质是通过自然指数函数构建的非线性映射，其定义可从双曲函数体系推导，核心表达式有两种常见形式，适配不同场景的计算与理解：
        """)
        st.markdown('### 1. tanh函数的数学表达式为： ###')
        st.latex(r"f(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}")
        st.markdown("""
        ### 2. tanh特点总结： ###
        - 输出范围：tanh 函数的输出范围被限制在 -1 到 1 之间，因此它可以使神经网络的输出更接近于零中心，有助于减少梯度消失问题。
        - 零中心性：tanh 函数的输出以零为中心，即在输入为 0 时函数值为 0，这有助于减少梯度消失问题，并使得神经网络更容易学习。
        ### 3. 相对于Sigmoid函数，优势显而易见： ###
        - 输出以零为中心：tanh函数的输出范围是-1到1之间，其均值为零，因此它是零中心的激活函数。相比于Sigmoid函数，tanh函数能够更好地处理数据的中心化和对称性，有助于提高网络的学习效率。 
        - 饱和区域更大：在输入绝对值较大时，tanh函数的斜率较大，这使得它在非线性变换上比Sigmoid函数更加陡峭，有助于提供更强的非线性特性，从而提高了网络的表达能力。 
        - 良好的输出范围：tanh函数的输出范围在-1到1之间，相比于Sigmoid函数的0到1之间，输出范围更广，有助于减少数据在网络中传播时的数值不稳定性。
        #### 但是缺点也同样明显： ####
        - 容易出现梯度消失问题：虽然相比于Sigmoid函数，tanh函数在非饱和区域的斜率较大，但在输入绝对值较大时，其导数仍然会接近于零，可能导致梯度消失问题。 
        - 计算难度同样大。 
        """)
        col1, col2 = st.columns([10, 1])
        with col2:
            submit = st.form_submit_button("我已了解")
        if submit:
            st.success("恭喜你，请往下查看代码示例")
            st.balloons()
    with st.form("tanh_2"):
        st.markdown("## 二、tanh函数图像 ##")
        sample_code = code_two.tanh_code()
        with st.expander("点击展开查看代码"):
            st.code(sample_code, language="python")
        col1, col2 = st.columns([6, 1])
        with col2:
            submit = st.form_submit_button("我已知晓，运行代码")
        if submit:
            st.success("已运行该代码，结果显示如下：")
            code_two.tanh_run()
elif choice == "ReLU函数":
    st.title("ReLU函数")
    with st.form("ReLU"):
        st.markdown("""
        ## 一、ReLU函数介绍 ##
        ReLU（Rectified Linear Unit，修正线性单元）是深度学习中应用最广泛的激活函数之一，由 Geoffrey Hinton 团队在 2010 年左右推广，
        彻底改变了神经网络的训练效率，尤其在卷积神经网络（CNN）和深度残差网络（ResNet）中成为默认选择。
        ReLU 函数的数学形式极其简洁，核心是 “保留正数值、将负数值置为 0”
        """)
        st.markdown('### 1. ReLU函数的数学表达式为： ###')
        st.latex(r"f(x) = max(0,x)")
        st.markdown("""
        ### 2. ReLU特点： ###
        - 稀疏性：ReLU 函数的导数在输入为负数时为零，这意味着在反向传播过程中，只有激活的神经元会传递梯度，从而促进了稀疏激活的现象，有助于减少过拟合。
        - 计算高效：ReLU 函数的计算非常简单，并且在实践中被证明是非常高效的。
        - 解决梯度消失问题： ReLU函数在输入大于零时输出其本身值，这使得在反向传播时梯度保持为常数1，避免了梯度消失问题。ReLU函数在深度网络中更容易训练。
        ### 3. 它的劣势： ###
        - 死亡ReLU问题（Dying ReLU）： 在训练过程中，某些神经元可能会遇到“死亡ReLU”问题，即永远不会被激活。如果某个神经元在训练过程中的权重更新导致在其上的输入始终为负值，那么它的输出将永远为零。
        这意味着该神经元不会再学习或参与后续训练，导致该神经元“死亡”，从而减少了网络的表达能力。
        ### 4. 死亡relu问题理解 ###
        ReLU函数梯度只可以取两个值，当输入小于0时，梯度为0；当输入大于0时，梯度为1，在反向传播过程中，（w新=w旧-学习率*梯度），如果学习率比较大，一个很大的梯度更新后，经过Relu激活函数，可能会导致ReLU神经元更新后的梯度是负数，进而导致下一轮正向传播过程中ReLU神经元的输入是负数，输出是0，由于ReLU神经元的输出为0，在后续迭代的反向过程中，该处的梯度一直为0，相关参数不再变化，从而导致ReLU神经元的输入始终是负数，输出始终为0。即为“死亡ReLU问题”。
        - 输出不是以零为中心： ReLU函数的输出范围是从零开始，因此输出不是以零为中心的。这可能会导致训练过程中的参数更新存在偏差，降低了网络的优化能力。
        - 不适合所有问题： 尽管ReLU函数在大多数情况下表现良好，但并不是适合所有问题。对于一些问题，特别是在处理一些包含负值的数据时，ReLU函数可能不够理想，可能会产生不良的结果。   
        """)
        st.markdown("针对ReLU函数的劣势，研究人员也提出了一些改进的激活函数，"
                    "如Leaky ReLU、Parametric ReLU和Exponential Linear Units（ELU）等，"
                    "这些函数在一定程度上缓解了ReLU函数的问题，并在特定情况下表现更好。因此，在实际使用中，根据具体问题和实验结果选择合适的激活函数是很重要的。")
        col1, col2 = st.columns([10, 1])
        with col2:
            submit = st.form_submit_button("我已了解")
        if submit:
            st.success("恭喜你，请往下查看代码示例")
            st.balloons()
    with st.form("ReLU_2"):
        st.markdown("## 二、ReLU函数图像 ##")
        sample_code = code_two.relu_code()
        with st.expander("点击展开查看代码"):
            st.code(sample_code, language="python")
        col1, col2 = st.columns([6, 1])
        with col2:
            submit = st.form_submit_button("我已知晓，运行代码")
        if submit:
            st.success("已运行该代码，结果显示如下：")
            code_two.relu_run()
elif choice == "Leaky ReLU函数":
    st.title("Leaky ReLU函数")
    with st.form("Leaky ReLU"):
        st.markdown("""
        ## 一、Leaky ReLU函数介绍 ##
        Leaky ReLU（Leaky Rectified Linear Unit，aky ReLU）是一种改进的ReLU函数
        """)
        st.image("./images/Leaky ReLU.png")
        col1, col2 = st.columns([10, 1])
        with col2:
            submit = st.form_submit_button("我已了解")
        if submit:
            st.success("恭喜你，请往下查看代码示例")
            st.balloons()
elif choice == "PReLU函数":
    st.title("PReLU函数")
    with st.form("PReLU"):
        st.markdown("""
        ## 一、PReLU函数介绍 ##
        PReLU（Parametric ReLU，参数化ReLU）是一种改进的ReLU函数
        """)
        st.image("./images/PReLU.png")
        col1, col2 = st.columns([10, 1])
        with col2:
            submit = st.form_submit_button("我已了解")
        if submit:
            st.success("恭喜你，请往下查看代码示例")
            st.balloons()
elif choice == "ELU函数":
    st.title("ELU函数")
    with st.form("ELU"):
        st.markdown("""
        ## 一、ELU函数介绍 ##
        ELU（Exponential Linear Unit，指数线性单元）激活函数是深度学习中用于增强网络学习能力的又一种激活函数，ELU通过在负值输入时提供负值输出，旨在结合Relu的优点和解决其潜在的一些问题，如ReLU的不活跃神经元问题。
        """)
        st.image("./images/ELU.png")
        st.markdown("""
        #### ELU优势： ####
        ELU通过在负数区域引入指数衰减，能够减少ReLU的“死亡”问题，同时保持负值的输出，有助于保持平均激活接近零，这有助于加快学习。
        #### ELU劣势： ####
        计算成本：由于ELU在负值时涉及指数运算，其计算成本高于Relu及其直接变体，尤其是在前向传播时。
        """)
        col1, col2 = st.columns([10, 1])
        with col2:
            submit = st.form_submit_button("我已了解")
        if submit:
            st.success("恭喜你，请往下查看代码示例")
            st.balloons()
    with st.form("ELU_2"):
        st.markdown("## 二、ELU函数图像 ##")
        sample_code = code_two.elu_code()
        with st.expander("点击展开查看代码"):
            st.code(sample_code, language="python")
        col1, col2 = st.columns([6, 1])
        with col2:
            submit = st.form_submit_button("我已知晓，运行代码")
        if submit:
            st.success("已运行该代码，结果显示如下：")
            code_two.elu_run()
elif choice == "Softmax函数":
    st.title("Softmax函数")
    with st.form("Softmax"):
        st.markdown("""
        ## 一、Softmax函数介绍 ##
        Softmax函数是常用的多分类激活函数，Softmax函数的输出是一个概率分布，即每个类别的概率。Softmax函数的输出是一个概率分布，即每个类别的概率。  
        softmax激活函数，但是很多地方，不会把softmax称为激活函数，但是呢没有一个合理的叫法，它就叫softmax函数，但是呢，它的性质上无论和我们的sigmoid或者tanh 或者relu等其实是类似的，我们可以把它称为激活函数。  
        softmax激活函数为什么不把它称为激活函数，是因为和它的位置是有关系的。为什么呢？  
        softmax这个函数和我们的sigmoid函数也好。relu函数也好，不一样的点在哪里？sigmoid函数一般位于算法的最后一层，softmax函数它一般只用于最后一次的激活，也就是输出之前的一次激活，前面（隐藏层）不用softmax。	  
        softmax一般不用于回归算法中，一般是用于分类中，我们前面计算的都是在算拟合和回归，softmax它做的是分类。  
        eg:猫和狗，当我们的输出不是一个值而是一个向量的时候，比如要分成三类(猫，狗 ，老虎)三类，根据输出的向量数值，按照下面的公式进行计算。
        """)
        st.markdown('### ELU函数的数学表达式为： ###')
        st.latex(r'softmax(x)_i = \frac{e^{x_i}}{\sum_{j=1}^{n}e^{x_j}}')
        st.markdown("""
        Softmax函数，可以将神经网络计算出来的数值通过公式变成概率，经过softmax后得到的结果相加和为1。另一个优势就是不论向量输出的结果是正值还是负值都能转化为正值。这个在后续的深度学习中用的非常多。
        ### softmax 特点总结： ###
        - 概率分布：Softmax函数将输入转换为概率分布，因此在多分类问题中常用于将模型的原始输出转换为概率值。
        - 连续可导：Softmax函数是连续可导的，这使得它可以与梯度下降等优化算法一起使用进行训练。
        - 指数增长：Softmax函数中的指数运算可能会导致数值稳定性问题，特别是当输入较大时。为了解决这个问题，可以通过减去输入向量总的最大值来进行数值稳定的计算。
        - 梯度计算简单：Softmax函数的导数计算相对简单，可以通过对Softmax函数的定义进行微分得到。
        """)
        col1, col2 = st.columns([10, 1])
        with col2:
            submit = st.form_submit_button("我已了解")
        if submit:
            st.success("恭喜你，请往下查看代码示例")
            st.balloons()
    with st.form("Softmax_2"):
        st.markdown("## 二、Softmax函数图像 ##")
        sample_code = code_two.softmax_code()
        with st.expander("点击展开查看代码"):
            st.code(sample_code, language="python")
        col1, col2 = st.columns([6, 1])
        with col2:
            submit = st.form_submit_button("我已知晓，运行代码")
        if submit:
            st.success("已运行该代码，结果显示如下：")
            code_two.softmax_run()
elif choice == "总结":
    st.title("总结")
    with st.form("总结"):
        st.markdown("""
        ### 总结： ###  
        #### 1.sigmoid 和 tanh：历史经典与局限性  ####
        - a.sigmoid 输出压缩到(0, 1)，适合概率输出。致命缺点：梯度消失。目前仅保留在二分类输出层（垃圾邮件分类等）。  
        - b.tanh输出以0为中心点范围是(-1, 1)，收敛速度快于sigmoid。缺点：依旧存在梯度消失问题。  
        #### 2.relu系列：简单高效的标杆  ####
        - a.基本relu   
            - ⅰ.f(x)=max(0, x)  
            - ⅱ.优点：计算简单高效，x>0的时候，梯度值为1，彻底解决梯度消失问题   
            - ⅲ.缺点：x<0的时候，梯度值为0，导致神经元死亡   
            - ⅳ.应用：CNN卷积层的标配，AlexNet提出，在ResNet中和残差连接配合使用效果极佳   
        - b.Leaky ReLu        
            - ⅰ.f(x) = max(αx, x) α是小常数  
            - ⅱ.优点：稍微了改进了死亡ReLu问题  
            - ⅲ.变种1：PReLU f(x) = max(αx, x) α是可学习的  
            - ⅳ.变种2：ELU 引入指数
        """)
        st.image("./images/img.png")
        col1, col2 = st.columns([10, 1])
        with col2:
            submit = st.form_submit_button("我已了解")
        if submit:
            st.success("恭喜你，你已完成本章知识点的学习")
            st.balloons()
